﻿using DotNetty.Buffers;
using DotNetty.Common.Internal.Logging;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using Microsoft.Extensions.Logging;
using NetC100K.Handler;
using NetC100K.Setting;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace NetC100K
{
    public class SessionPool
    {
        private ILogger _logger;

        private Bootstrap _bootstrap;
        private MultithreadEventLoopGroup _group;
        private ConcurrentDictionary<string, IChannel> _channels;
        public ConcurrentDictionary<string, IChannel> Channels { get { return _channels; } }

        public SessionPool(ILoggerFactory factory)
        {
            try
            {
                _logger = factory.CreateLogger<SessionPool>();

                //InternalLoggerFactory.DefaultFactory = factory;
                Environment.SetEnvironmentVariable("io.netty.allocator.numHeapArenas", "0");
                Environment.SetEnvironmentVariable("io.netty.allocator.numDirectArenas", "0");

                _channels = new ConcurrentDictionary<string, IChannel>();
                _group = new MultithreadEventLoopGroup();
                _bootstrap = new Bootstrap();
                _bootstrap.Group(_group)
                    .Channel<TcpSocketChannel>()
                    .Option(ChannelOption.TcpNodelay, true)
                    .Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
                    {
                        IChannelPipeline pipeline = channel.Pipeline;
                        pipeline.AddLast(new SessionHandler(_channels, _logger));
                    }));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "SessionPool初始化时发生错误");
            }
        }

        public async void ConnectAsync(IPEndPoint remoteAddress, IPEndPoint localAddress)
        {
            try
            {
                await _bootstrap.ConnectAsync(remoteAddress, localAddress);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "建立连接时发生错误");
            }
        }

        public void ConcurrentSend(Func<IChannel, string> action)
        {
            var channels = _channels.Values;
            foreach (var channel in channels)
            {
                try
                {
                    if (channel != null)
                    {
                        string message = action(channel);
                        byte[] bytes = System.Text.Encoding.UTF8.GetBytes($"{ message}*");
                        IByteBuffer initialMessage = Unpooled.CopiedBuffer(bytes);
                        channel.WriteAndFlushAsync(initialMessage);
                    }
                    else
                    {
                        _logger.LogWarning("发现未知连接，已经删除");
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"循环发送时发生错误");
                }
            }
        }

        public async Task Close()
        {
            if (_group != null) await _group.ShutdownGracefullyAsync();
        }

        public async void Dispose()
        {
            await Close();
        }
    }
}
